#include <stdlib.h>
#include <stdio.h>

#include "include/sync.h"
#include "include/settings.h"
#include "include/sock_lib.h"
#include "include/thread_pool.h"
#include "include/defs.h"

#ifdef _WIN32
#include "pthreads/include/pthread.h"
#else
#include <pthread.h>
#endif

/////////////////////////////////
// Used by all data structures //
/////////////////////////////////

unsigned int length = 0;

///////////////////////
// Linked List Queue //
///////////////////////

typedef struct QUEUE {
    int socket;
    struct QUEUE* next;
} qMember;

qMember* qHead = NULL;
qMember* qTail = NULL;

void qAdd(int socket) {
    qMember* newNode = malloc(sizeof(qMember));

    VPRINTF(("Adding socket: %d\n", socket));

    if (!newNode) { printf("Malloc failed, qAdd\n"); exit(1); }

    newNode->next = NULL;
    newNode->socket = socket;

    pthread_mutex_lock(get_mutex());

    if (!qHead) {
        qTail = qHead = newNode;
        length++;
    } else {
        qTail->next = newNode;
        qTail = qTail->next;
        length++;
    }

    pthread_mutex_unlock(get_mutex());
    pthread_cond_signal(get_is_ready());
    
}

int qRemove() {
    int toRet;                                  //Socket to return
    qMember* temp;

    pthread_mutex_lock(get_mutex());                 //Lock mutex

    while (!qHead) {
        pthread_cond_wait( get_is_ready(), get_mutex() );  //No requests waiting
    }

    toRet = qHead->socket;                      //retrieve socket
    VPRINTF(("Removing socket: %d\n", toRet));
        
    //Fix the list
    temp = qHead;                                           
    qHead = qHead->next;
    length--;

    //Fine grain lock, release lock before freeing old node
    pthread_mutex_unlock(get_mutex());

    free(temp);    
        
    return toRet;    
}

/*
 * Wrappers
 */
void addSocket(int socket) {
    #ifdef LLQ
        qAdd(socket);
    #else
        qAdd(socket);
    #endif
}

int removeSocket() {
    #ifdef LLQ
        return qRemove();
    #else
        return qRemove();
    #endif
}

unsigned int size() {
    return length;
}

#ifdef _THREAD_POOL_

void init_thread_pool(settings_t *s) {
    int i;

    printf("** Initializing Thread Pool\n");
    fflush(stdout);
   
    //initialize producer
    producerThread = malloc(sizeof(pthread_t));
    if (!producerThread) { exit(1); }

    printf("*** Listening on Port: %d\n", s->PORT_NUMBER);

    pthread_create(producerThread, NULL, producer, &(s->PORT_NUMBER));

    //Initialize consumers
    consumerList = malloc(sizeof(pthread_t*)*s->MAX_THREADS);

    for (i=0; i < s->MAX_THREADS; i++) {
        consumerList[i] = malloc(sizeof(pthread_t));
        
        if (pthread_create(consumerList[i], NULL, consumer, NULL)) {
            printf("Error creating thread %d\n",i);
        }

    }

}

#endif
